package com.atguigu.gmall.common.config;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.common.model.GmallCorrelationData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;

import javax.annotation.PostConstruct;
import java.util.concurrent.TimeUnit;

@Configuration
@Slf4j
public class MQProducerAckConfig implements RabbitTemplate.ConfirmCallback,RabbitTemplate.ReturnCallback{

    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private RedisTemplate redisTemplate;

    //初始化
    @PostConstruct
    public void init(){
        rabbitTemplate.setConfirmCallback(this);
        rabbitTemplate.setReturnCallback(this);
    }
    //判断这个消息是否到了交换机
    /**
     * correlationData：封装了一个消息，这个消息有自定义的id标识。
     * ack：  true 消息到了交换机  false 消息没有到交换机
     * codeMsg： 原因
     * */
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String codeMsg) {
        if (ack){
            log.info("消息到交换机");
            System.out.println(codeMsg);
        }else{
            log.error("消息没有到交换机");
            System.out.println(codeMsg);
            this.retrySendMsg(correlationData);
        }
    }



    /**
     * 判断这个消息是否到了队列  -- 消息没有到队列，才会执行这个方法
     * @param message       消息主体
     * @param replyCode     应答码 返回码
     * @param replyText     原因  返回码对应的内容
     * @param exchange      交换机
     * @param routingKey    路由键
     */
    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
        // 反序列化对象输出
        System.out.println("消息主体: " + new String(message.getBody()));
        System.out.println("应答码: " + replyCode);
        System.out.println("描述：" + replyText);
        System.out.println("消息使用的交换器 exchange : " + exchange);
        System.out.println("消息使用的路由键 routing : " + routingKey);
//        if ("".equals(exchange)&&"".equals(routingKey)){
//            return;
//        }

        //消息没有到队列时走这个方法
        String correlationDataId = (String) message.getMessageProperties().getHeaders().get("spring_returned_message_correlation");
        //从redis获取数据
        String strJson = (String) this.redisTemplate.opsForValue().get(correlationDataId);
        //转换为对象
        GmallCorrelationData gmallCorrelationData = JSON.parseObject(strJson, GmallCorrelationData.class);
        this.retrySendMsg(gmallCorrelationData);
    }


    //自定义重试方法
    private void retrySendMsg(CorrelationData correlationData) {
        //
        GmallCorrelationData gmallCorrelationData = (GmallCorrelationData) correlationData;
        //重试次数
        int retryCount = gmallCorrelationData.getRetryCount();
        System.out.println("retryCount = " + retryCount);
        if (retryCount>=3){
            //不需要重试，直接将数据写入数据表
            log.error("重试次数已到，将数据持久化");
        }else{
            retryCount++;
            //写回缓存
            gmallCorrelationData.setRetryCount(retryCount);
            //覆盖缓存中的数据
            redisTemplate.opsForValue().set(gmallCorrelationData.getId(),JSON.toJSONString(gmallCorrelationData),10, TimeUnit.MINUTES);
            //
            if (gmallCorrelationData.isDelay()){
                //属于延迟消息
                rabbitTemplate.convertAndSend(gmallCorrelationData.getExchange(),gmallCorrelationData.getRoutingKey(),gmallCorrelationData.getMessage(),(message) -> {
                    //设置延迟时间 delayTime穿几就是几秒 1 = 1秒
                    message.getMessageProperties().setDelay(gmallCorrelationData.getDelayTime()*1000);
//                    message.getMessageProperties().set
                    return message;
                },gmallCorrelationData);
            }
            //重试,再次发送消息 使用rabbitTemplate
            this.rabbitTemplate.convertAndSend(gmallCorrelationData.getExchange(),gmallCorrelationData.getRoutingKey(),gmallCorrelationData.getMessage(),gmallCorrelationData);

        }
    }
}
